﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;

namespace Xeml.Document.Ontologies.OBO
{
    public class ParsedOboFile : List<OboStanza>
    {
        #region members
        public string DataVersion { get; private set; }
        public string DefaultNamespace { get; private set; }

        private static Regex _rxDocumentHeaderTag = new System.Text.RegularExpressions.Regex(@"^(default-namespace:\s*(?<defaultnamespace>.+))|(data-version:\s*(?<dataversion>.+))$", RegexOptions.Compiled);
        #endregion members

        internal ParsedOboFile()
            : base()
        {
        }

        internal ParsedOboFile(System.IO.StringReader reader)
            : this()
        {
            this.Add(GetOboEntries(reader, this.AddDocumentHeaderTag).AsParallel().AsUnordered());

            //silently removing empty attributeLists, something read from the reader with missing content
            for (int i = base.Count - 1; i >= 0; i--)
            {
                if (base[i].Count == 0)
                {
#if DEBUG
                    //System.Diagnostics.Debugger.Break();
#endif
                    base.RemoveAt(i);
                }
            }
        }

        internal IEnumerable<OboStanza> GetValidTags()
        {
            return this.Where(x => !x.isObsolete);
        }

        private void Add(IEnumerable<System.IO.Stream> source)
        {
            this.AddRange(source.Select(x => new OboStanza(x)));
        }

        private void AddDocumentHeaderTag(string headerInfo)
        {
            GroupCollection matches = _rxDocumentHeaderTag.Match(headerInfo).Groups;

            if (matches["defaultnamespace"].Success)
            {
                this.DefaultNamespace = matches["defaultnamespace"].Value;
            }
            else if (matches["dataversion"].Success)
            {
                this.DataVersion = matches["dataversion"].Value;
            }
        }

        private IEnumerable<Stream> GetOboEntries(StringReader textreader, Action<string> callbackDocumentHeaderTag)
        {
            bool outStreamStarted = false;
            bool bufferFilled = false;
            StreamWriter buffer = new StreamWriter(new MemoryStream());
            string line = string.Empty;
            while (textreader.Peek() != -1)
            {
                line = textreader.ReadLine().Trim();
                Match m = OboStanza.rxOboStanzaTypes.Match(line);
                if (m.Success && bufferFilled && outStreamStarted)
                {
                    buffer.Flush();
                    buffer.BaseStream.Position = 0;
                    yield return buffer.BaseStream;
                    buffer = new StreamWriter(new MemoryStream());
                    bufferFilled = false;
                }
                if (line.Length > 0 && !line.StartsWith("!"))
                {
                    if (m.Success)
                    {
                        outStreamStarted = true;
                    }
                    else
                    {
                        if (!outStreamStarted)
                        {
                            //we assume that anything before the first ... belongs to the Document Header Tags 
                            callbackDocumentHeaderTag(line);
                        }
                    }
                    buffer.WriteLine(line);
                    bufferFilled = true;
                }
            }

            //submit the last chunck of data
            if (bufferFilled)
            {
                buffer.Flush();
                buffer.BaseStream.Position = 0;
                yield return buffer.BaseStream;
            }
            yield break;
        }

        [Obsolete("just for demonstration purposes on how to reset a reader", false)]
        private void ResetReader(TextReader reader)
        {
            (reader as StreamReader).BaseStream.Seek(0, SeekOrigin.Begin);
            (reader as StreamReader).DiscardBufferedData();
        }
    }
}
